﻿using System.Collections.Generic;
using System.Data.SqlClient;
using Bussiness.Exception;
using Bussiness.Model;
using Bussiness.Utilities;
using NHibernate;
using NHibernate.Criterion;

namespace Bussiness.Service.Base
{
    internal abstract class BaseService<T, TId> : IDAO<T, TId>
    {
        protected ISession _session;

        protected ISession Session
        {
            get
            {
                if (_session == null || !_session.IsOpen) _session = HibernateHelper.GetCurrentSession();
                return _session;
            }
        }

        #region IDAO<T, TId> members.

        public T FindById(TId id)
        {
            try
            {
                return Session.Get<T>(id);
            }
            catch (HibernateException ex)
            {
                throw new InfrastructureException("Cannot get entity!", ex);
            }
        }

        public T FetchById(TId id)
        {
            try
            {
                return Session.Load<T>(id);
            }
            catch (HibernateException ex)
            {
                throw new InfrastructureException("Cannot load entity!", ex);
            }
        }

        public IList<T> FindAll()
        {
            return FindByCriteria();
        }

        public IList<T> FindByExample(T exampleInstance, params string[] excludeProperties)
        {
            ICriteria criteria = Session.CreateCriteria(typeof (T));
            Example example = Example.Create(exampleInstance);
            foreach (string exclude in excludeProperties)
            {
                example.ExcludeProperty(exclude);
            }
            criteria.Add(example);
            return criteria.List<T>();
        }

        public T MakePersistance(T entity)
        {
            try
            {
                Session.SaveOrUpdate(entity);
            }
            catch (System.Exception ex)
            {
                throw new BussinessException("Cannot insert entity to database", ex);
            }

            return entity;
        }

        public void MakeTransient(T entity)
        {
            Session.Delete(entity);
        }

        public void Flush()
        {
            Session.Flush();
        }

        public void Clear()
        {
            Session.Clear();
        }

        public IList<T> FindByCriteria(ICriteria criteria, int start, int range)
        {
            return criteria.SetFirstResult(start).SetMaxResults(range).List<T>();
        }

        public int CountByCriteria(ICriteria criteria)
        {
            return (int) criteria.SetProjection(Projections.RowCount()).UniqueResult();
        }

        #endregion

        /// <summary>
        /// Use this inside subclasses as a convenience method.
        /// </summary>
        /// <param name="criterias"></param>
        /// <returns></returns>
        protected IList<T> FindByCriteria(params ICriterion[] criterias)
        {
            ICriteria criteria = Session.CreateCriteria(typeof (T));
            foreach (ICriterion criterion in criterias)
            {
                criteria.Add(criterion);
            }
            return criteria.List<T>();
        }

        protected ICriteria CreateCriteria()
        {
            return Session.CreateCriteria(typeof (T));
        }
    }
}